package de.mmis.core.base;

import java.lang.reflect.Constructor;

import org.apache.log4j.Logger;

import de.mmis.core.base.abstracttree.InnerNode;
import de.mmis.core.base.abstracttree.Tree;
import de.mmis.core.base.abstracttree.Tree.Type;
import de.mmis.core.base.abstracttree.serializer.DeserializableException;

/**
 * {@link Exception} subclass used when method invocations by NWProxy or TSProxy
 * failed. Provides a static method to generate the Exception from the given
 * Exception {@link Tree}.
 * 
 * @author Christian Koch
 * 
 */
public class ProxyException extends Exception {
	private final static Logger LOGGER = Logger.getLogger(ProxyException.class);

	private static final long serialVersionUID = -4031241840272244157L;

	private Tree exp;

	ProxyException(Tree exp) {
		super(exp.toString());
		this.exp = exp;
	}

	ProxyException(Tree exp, Throwable cause) {
		super(exp.toString(), cause);
		this.exp = exp;
	}

	/**
	 * @return error {@link Tree} returned
	 */
	public Tree getTree() {
		return this.exp;
	}

	/**
	 * generates an Exception from a given error {@link Tree}
	 * 
	 * @param errexp
	 * @param classLoader
	 *            {@link ClassLoader} used to load exception class
	 * @return generated Exception, or default {@link ProxyException} in case it
	 *         cannot be generated
	 */
	public static Throwable generateException(Tree errexp,
			ClassLoader classLoader) {
		// check error expression structure
		if (errexp == null || errexp.getType() != Type.INNER
				|| ((InnerNode) errexp).getNumberOfSubTrees() != 4)
			return new ProxyException(errexp);
		// get exception information
		Class<?> exceptionClass;
		String exceptionMessage;
		try {
			exceptionClass = ((InnerNode) errexp).getSubTree(1).getAs(
					Class.class, classLoader);
			exceptionMessage = ((InnerNode) errexp).getSubTree(2).getAsString();
		} catch (DeserializableException e2) {
			LOGGER.error(e2, e2);
			return new ProxyException(errexp);
		}
		try // try to generate exception, return default exception in case
			// of failure
		{
			try {
				return (Throwable) ((InnerNode) errexp).getSubTree(3).getAs(
						exceptionClass, classLoader);
			} catch (DeserializableException e1) {
				try {
					Constructor<?> cons = exceptionClass
							.getConstructor(String.class);
					return (Throwable) cons.newInstance(exceptionMessage);
				} catch (NoSuchMethodException e) {
					return (Throwable) exceptionClass.newInstance();
				}
			}
		} catch (Exception e) {
			return new ProxyException(errexp);
		}
	}

}
